module chess {
	export class ChessBoard extends BaseClass {
		public constructor() {
			super();
			this.zobrist = new chess.Zobrist(15);
			this.statistic = new chess.Statistic();
		}

		public currentSteps: Array<any>;
		public allSteps: Array<any>;
		public stepsTail: Array<any>;
		public zobrist: chess.Zobrist;
		public statistic: chess.Statistic;

		public count: number;
		public total: number;

		public board: Array<any>;
		public comScore: Array<any>;
		public humScore: Array<any>;
		public scoreCache: Array<any>;

		public comMaxScore: number = 0;
		public humMaxScore: number = 0;


		public init(sizeOrBoard) {
			let self = this;
			self.currentSteps = []; // 当前一次思考的步骤
			self.allSteps = [];
			self.stepsTail = [];

			self.zobrist.init() // 注意重新初始化
			self.count = 0;		// chessman count
			let size
			if (sizeOrBoard.length) {
				self.board = sizeOrBoard;
				size = self.board.length;
				for (let i = 0; i < self.board.length; i++) self.count += self.board[i].filter(d => d > 0).length;
			} else {
				size = sizeOrBoard;
				self.board = [];
				for (let i = 0; i < size; i++) {
					let row = [];
					for (let j = 0; j < size; j++) {
						row.push(0)
					}
					self.board.push(row)
				}
			}
			self.statistic.init(size);


			// 存储双方得分
			self.comScore = ai.ChessMath.getInstance().createArray(size, size);
			self.humScore = ai.ChessMath.getInstance().createArray(size, size);
			self.scoreCache = [
				[], // placeholder
				[ // for role 1
					ai.ChessMath.getInstance().createArray(size, size),
					ai.ChessMath.getInstance().createArray(size, size),
					ai.ChessMath.getInstance().createArray(size, size),
					ai.ChessMath.getInstance().createArray(size, size)
				],
				[ // for role 2
					ai.ChessMath.getInstance().createArray(size, size),
					ai.ChessMath.getInstance().createArray(size, size),
					ai.ChessMath.getInstance().createArray(size, size),
					ai.ChessMath.getInstance().createArray(size, size)
				]
			]

			self.initScore();
		}

		public initScore() {
			let board = this.board
			for (let i = 0; i < board.length; i++) {
				for (let j = 0; j < board[i].length; j++) {
					// 空位，对双方都打分
					if (board[i][j] == chess.ChessConst.role.empty) {
						if (this.hasNeighbor(i, j, 2, 2)) { //必须是有邻居的才行
							let cs = chess.BoardUtils.scorePoint(this, i, j, chess.ChessConst.role.com)
							let hs = chess.BoardUtils.scorePoint(this, i, j, chess.ChessConst.role.hum)
							this.comScore[i][j] = cs
							this.humScore[i][j] = hs
						}

					} else if (board[i][j] == chess.ChessConst.role.com) { // 对电脑打分，玩家此位置分数为0
						this.comScore[i][j] = chess.BoardUtils.scorePoint(this, i, j, chess.ChessConst.role.com)
						this.humScore[i][j] = 0
					} else if (board[i][j] == chess.ChessConst.role.hum) { // 对玩家打分，电脑位置分数为0
						this.humScore[i][j] = chess.BoardUtils.scorePoint(this, i, j, chess.ChessConst.role.hum)
						this.comScore[i][j] = 0
					}
				}
			}
		}

		public hasNeighbor(x, y, distance, count) {
			let board = this.board
			let len = board.length
			let startX = x - distance
			let endX = x + distance
			let startY = y - distance
			let endY = y + distance
			for (let i = startX; i <= endX; i++) {
				if (i < 0 || i >= len) continue
				for (let j = startY; j <= endY; j++) {
					if (j < 0 || j >= len) continue
					if (i == x && j == y) continue
					if (board[i][j] != chess.ChessConst.role.empty) {
						count--
						if (count <= 0) return true
					}
				}
			}
			return false
		}

		public gen(role, onlyThrees?, starSpread?) {
			if (this.count <= 0) return [7, 7]
			let fives = []
			let comfours = []
			let humfours = []
			let comblockedfours = []
			let humblockedfours = []
			let comtwothrees = []
			let humtwothrees = []
			let comthrees = []
			let humthrees = []
			let comtwos = []
			let humtwos = []
			let neighbors = []

			let R = chess.ChessConst.role;
			let config = chess.ChessConst.config;
			let S = chess.ChessConst.score;

			let board = this.board
			let reverseRole = R.reverse(role);
			// 找到双方的最后进攻点
			const attackPoints = [] // 进攻点
			const defendPoints = [] // 防守点


			// 默认情况下 我们遍历整个棋盘。但是在开启star模式下，我们遍历的范围就会小很多
			// 只需要遍历以两个点为中心正方形。
			// 注意除非专门处理重叠区域，否则不要把两个正方形分开算，因为一般情况下这两个正方形会有相当大的重叠面积，别重复计算了
			if (starSpread && config.star) {
				let i = this.currentSteps.length - 1
				while (i >= 0) {
					let p = this.currentSteps[i]
					if (reverseRole === R.com && p.scoreCom >= S.THREE
						|| reverseRole === R.hum && p.scoreHum >= S.THREE) {
						defendPoints.push(p)
						break
					}
					i -= 2
				}

				i = this.currentSteps.length - 2
				while (i >= 0) {
					let p = this.currentSteps[i]
					if (role === R.com && p.scoreCom >= S.THREE
						|| role === R.hum && p.scoreHum >= S.THREE) {
						attackPoints.push(p)
						break;
					}
					i -= 2
				}

				if (!attackPoints.length) attackPoints.push(this.currentSteps[0].role === role ? this.currentSteps[0] : this.currentSteps[1])
				if (!defendPoints.length) defendPoints.push(this.currentSteps[0].role === reverseRole ? this.currentSteps[0] : this.currentSteps[1])
			}

			for (let i = 0; i < board.length; i++) {
				for (let j = 0; j < board.length; j++) {
					if (board[i][j] == R.empty) {

						if (this.allSteps.length < 6) {
							if (!this.hasNeighbor(i, j, 1, 1)) continue
						} else if (!this.hasNeighbor(i, j, 2, 2)) continue

						let scoreHum = this.humScore[i][j]
						let scoreCom = this.comScore[i][j]
						let maxScore = Math.max(scoreCom, scoreHum)

						if (onlyThrees && maxScore < S.THREE) continue

						let p = [i, j]
						p["scoreHum"] = scoreHum
						p["scoreCom"] = scoreCom
						p["score"] = maxScore
						p["role"] = role

						this.total++;
						/* 双星延伸，以提升性能
						 * 思路：每次下的子，只可能是自己进攻，或者防守对面（也就是对面进攻点）
						 * 我们假定任何时候，绝大多数情况下进攻的路线都可以按次序连城一条折线，那么每次每一个子，一定都是在上一个己方棋子的八个方向之一。
						 * 因为既可能自己进攻，也可能防守对面，所以是最后两个子的米子方向上
						 * 那么极少数情况，进攻路线无法连成一条折线呢?很简单，我们对前双方两步不作star限制就好，这样可以 兼容一条折线中间伸出一段的情况
						 */
						if (starSpread && config.star) {
							let roleScore = role === R.com ? p["scoreCom"] : p["scoreHum"];
							let deRoleScore = role === R.com ? p["scoreHum"] : p["scoreCom"];

							if (maxScore >= S.FOUR) {
							} else if (maxScore >= S.BLOCKED_FOUR && chess.BoardUtils.starTo(this.currentSteps[this.currentSteps.length - 1])) {
								//star 路径不是很准，所以考虑冲四防守对手最后一步的棋
							} else if (
								chess.BoardUtils.starTo(p, attackPoints) || chess.BoardUtils.starTo(p, defendPoints)
							) {
							} else {
								this.count++
								continue
							}
						}

						if (scoreCom >= S.FIVE) {//先看电脑能不能连成5
							fives.push(p)
						} else if (scoreHum >= S.FIVE) {//再看玩家能不能连成5
							//别急着返回，因为遍历还没完成，说不定电脑自己能成五。
							fives.push(p)
						} else if (scoreCom >= S.FOUR) {
							comfours.push(p)
						} else if (scoreHum >= S.FOUR) {
							humfours.push(p)
						} else if (scoreCom >= S.BLOCKED_FOUR) {
							comblockedfours.push(p)
						} else if (scoreHum >= S.BLOCKED_FOUR) {
							humblockedfours.push(p)
						} else if (scoreCom >= 2 * S.THREE) {
							//能成双三也行
							comtwothrees.push(p)
						} else if (scoreHum >= 2 * S.THREE) {
							humtwothrees.push(p)
						} else if (scoreCom >= S.THREE) {
							comthrees.push(p)
						} else if (scoreHum >= S.THREE) {
							humthrees.push(p)
						} else if (scoreCom >= S.TWO) {
							comtwos.unshift(p)
						} else if (scoreHum >= S.TWO) {
							humtwos.unshift(p)
						} else neighbors.push(p)
					}
				}
			}

			//如果成五，是必杀棋，直接返回
			if (fives.length) return fives

			// 自己能活四，则直接活四，不考虑冲四
			if (role === R.com && comfours.length) return comfours
			if (role === R.hum && humfours.length) return humfours

			// 对面有活四冲四，自己冲四都没，则只考虑对面活四 （此时对面冲四就不用考虑了)

			if (role === R.com && humfours.length && !comblockedfours.length) return humfours
			if (role === R.hum && comfours.length && !humblockedfours.length) return comfours

			// 对面有活四自己有冲四，则都考虑下
			let fours = role === R.com ? comfours.concat(humfours) : humfours.concat(comfours)
			let blockedfours = role === R.com ? comblockedfours.concat(humblockedfours) : humblockedfours.concat(comblockedfours)
			if (fours.length) return fours.concat(blockedfours)

			let result = []
			if (role === R.com) {
				result =
					comtwothrees
						.concat(humtwothrees)
						.concat(comblockedfours)
						.concat(humblockedfours)
						.concat(comthrees)
						.concat(humthrees)
			}
			if (role === R.hum) {
				result =
					humtwothrees
						.concat(comtwothrees)
						.concat(humblockedfours)
						.concat(comblockedfours)
						.concat(humthrees)
						.concat(comthrees)
			}

			// result.sort(function(a, b) { return b.score - a.score })
			//双三很特殊，因为能形成双三的不一定比一个活三强
			if (comtwothrees.length || humtwothrees.length) {
				return result
			}


			// 只返回大于等于活三的棋
			if (onlyThrees) {
				return result
			}


			let twos
			if (role === R.com) twos = comtwos.concat(humtwos)
			else twos = humtwos.concat(comtwos)

			twos.sort(function (a, b) { return b.score - a.score })
			result = result.concat(twos.length ? twos : neighbors)

			//这种分数低的，就不用全部计算了
			if (result.length > config.countLimit) {
				return result.slice(0, config.countLimit)
			}

			return result
		}

		/**
		 * 只更新一个点附近的分数
  		 *参见 evaluate point 中的代码，为了优化性能，在更新分数的时候可以指定只更新某一个方向的分数
  		*/
		public updateScore(p) {
			let radius = 4,
				board = this.board,
				self = this,
				len = this.board.length;
			let R = chess.ChessConst.role;
			function update(x, y, dir) {
				let role = self.board[x][y];
				if (role !== R.reverse(R.com)) {
					let cs = chess.BoardUtils.scorePoint(self, x, y, R.com, dir);
					self.comScore[x][y] = cs;
					self.statistic.table[x][y] += cs;
				} else self.comScore[x][y] = 0;
				if (role !== R.reverse(R.hum)) {
					let hs = chess.BoardUtils.scorePoint(self, x, y, R.hum, dir);
					self.humScore[x][y] = hs;
					self.statistic.table[x][y] += hs;
				} else self.humScore[x][y] = 0;

			}
			// 无论是不是空位 都需要更新
			// -
			for (let i = -radius; i <= radius; i++) {
				let x = p[0], y = p[1] + i
				if (y < 0) continue
				if (y >= len) break
				update(x, y, 0)
			}

			// |
			for (let i = -radius; i <= radius; i++) {
				let x = p[0] + i, y = p[1]
				if (x < 0) continue
				if (x >= len) break
				update(x, y, 1)
			}

			// \
			for (let i = -radius; i <= radius; i++) {
				let x = p[0] + i, y = p[1] + i
				if (x < 0 || y < 0) continue
				if (x >= len || y >= len) break
				update(x, y, 2)
			}

			// /
			for (let i = -radius; i <= radius; i++) {
				let x = p[0] + i, y = p[1] - i
				if (x < 0 || y < 0) continue
				if (x >= len || y >= len) continue
				update(x, y, 3)
			}
		}

		/**棋面估分
  		*这里只算当前分，而不是在空位下一步之后的分
		*/
		public evaluate(role) {
			// 这里都是用正整数初始化的，所以初始值是0
			this.comMaxScore = 0;
			this.humMaxScore = 0;

			let board = this.board;
			let R = chess.ChessConst.role;

			//遍历出最高分，开销不大
			for (let i = 0; i < board.length; i++) {
				for (let j = 0; j < board[i].length; j++) {
					if (board[i][j] == R.com) {
						this.comMaxScore += chess.BoardUtils.fixScore(this.comScore[i][j]);
					} else if (board[i][j] == R.hum) {
						this.humMaxScore += chess.BoardUtils.fixScore(this.humScore[i][j]);
					}
				}
			}
			// 有冲四延伸了，不需要专门处理冲四活三
			// 不过这里做了这一步，可以减少电脑胡乱冲四的毛病
			//this.comMaxScore = fixScore(this.comMaxScore)
			//this.humMaxScore = fixScore(this.humMaxScore)
			let result = (role == R.com ? 1 : -1) * (this.comMaxScore - this.humMaxScore);
			return result;
		}

		//下子
		public put(p, role) {
			p.role = role;
			// console.log('put [' + p + ']' + ' ' + role);
			this.board[p[0]][p[1]] = role;
			this.zobrist.go(p[0], p[1], role);
			this.updateScore(p);
			this.allSteps.push(p);
			this.currentSteps.push(p);
			this.stepsTail = [];
			this.count++;
		}

		//移除棋子
		public remove(p) {
			let r = this.board[p[0]][p[1]]
			chess.ChessConst.config.debug && Log.trace('remove [' + p + ']' + ' ' + r)
			this.zobrist.go(p[0], p[1], r);
			this.board[p[0]][p[1]] = chess.ChessConst.role.empty;
			this.updateScore(p);
			this.allSteps.pop();
			this.currentSteps.pop();
			this.count--;
		}

		//悔棋
		public backward() {
			if (this.allSteps.length < 2) return;
			let i = 0;
			while (i < 2) {
				let s = this.allSteps[this.allSteps.length - 1];
				this.remove(s);
				this.stepsTail.push(s);
				i++
			}
		}

		//前进
		public forward() {
			if (this.stepsTail.length < 2) return;
			let i = 0;
			while (i < 2) {
				let s = this.stepsTail.pop();
				this.put(s, s.role);
				i++;
			}
		}

		public logSteps() {
			Log.trace("steps:" + this.allSteps.map((d) => '[' + d[0] + ',' + d[1] + ']').join(','));
		}

		public toString() {
			return this.board.map(function (d) { return d.join(',') }).join('\n');
		}

	}
}